/*!
 *	$Id: sdcard.h 256 2011-07-20 07:14:30Z podolski $
 *	$URL: https://versions1.kt.uni-due.de/svn/ktdsp/trunk/sdcard.h $
 *	$Revision: 256 $
 *	$Date: 2011-07-20 09:14:30 +0200 (Mi, 20 Jul 2011) $
 *	$Author: podolski $
 *	@author Lehrstuhl fuer Kommunikationstechnik, Universitaet Duisburg-Essen
 *	@file
 *	@brief SD card driver.
 *
 *	This module allows to write to and read data from an SD card.
 *
 */

#include <sdcard.h>
#include <config.h>
#include <stm_spi.h>



/**************************************************************
 *********** LOCAL DEFINITIONS *********
 **************************************************************/
static void sdcard_spi_write_byte(SdcardHandle *h, uint8_t data);
static void sdcard_spi_send_frame(SdcardHandle *h, uint8_t *data, uint16_t size) ;
static void sdcard_send_cmd(SdcardHandle *h, uint8_t cmd, uint32_t data, uint8_t crc);
static uint8_t sdcard_get_response(SdcardHandle *h);
static uint8_t sdcard_get_byte(SdcardHandle *h);
static void sdcard_get_multibyte_response(SdcardHandle *h, uint8_t *data, uint8_t size);



/**************************************************************
 *********** LOCAL FUNCTIONS *********
 **************************************************************/
static void sdcard_wait_for_ready(SdcardHandle *h) {
	uint32_t timeout = 0xfffe;
	uint8_t data_token;
	do {
		data_token = sdcard_get_byte(h);
		sdcard_spi_write_byte(h, 0xFF);
		--timeout;
	} while ((data_token != 0xFF) && (timeout != 0));
}

/**************************************************************
 *********** function sends single byte to the sdcard *********
 **************************************************************/

static void sdcard_spi_write_byte(SdcardHandle *h, uint8_t data) {
	uint8_t dummy;
	dummy = sdcard_spi_transfer(h, data);
}

/*************************************************************************
 *********** function sends a byte frame of any size to sd card  *********
 *************************************************************************/

static void sdcard_spi_send_frame(SdcardHandle *h, uint8_t *data, uint16_t size) {
	uint16_t i;
	for (i = 0; i < size; ++i) {
		sdcard_spi_write_byte(h, data[i]);
	}
}

/******************************************************************************************
 *********** function sends command to sd card, commands are defined in sdcard.h  *********
 ******************************************************************************************/

static void sdcard_send_cmd(SdcardHandle *h, uint8_t cmd, uint32_t address,
		uint8_t crc) { //cmd - arguments of command are defined
	uint8_t frame[6];
	uint8_t temp;
	int8_t i;

	frame[0] = cmd;
	for (i = 3; i >= 0; i--) {
		temp = (uint8_t) (address >> (8 * i));
		frame[4 - i] = temp;
	}
	frame[5] = crc;
	sdcard_spi_send_frame(h, frame, 6);
}




/*******************************************************
 *********** function gets a byte from miso pin ********
 *******************************************************/

static uint8_t sdcard_get_byte(SdcardHandle *h) {
	uint8_t data;
	data = sdcard_spi_transfer(h, 0xFF);
	return data;
}

/******************************************************************************************************************************************
 *********** function gets any byte response from miso pin (type of response - R2 or R3, using to reading sd card internal registers*******
 ******************************************************************************************************************************************/

static uint8_t sdcard_get_response(SdcardHandle *h) {
	uint8_t temp = 0xFF;
	uint8_t i = 0;

	//Response comes 1-8bytes after command
	//the first bit will be a 0
	//followed by an error code
	//data will be 0xff until response

	do {
		temp = sdcard_get_byte(h);
		if (~temp & 0x80) {
			return temp;
		}
		++i;
	} while (i < 8);

	return temp;
}

/************************************************************************************************
 *********** function gets a one byte response from miso pin (type of response - R1 or R1b*******
 ************************************************************************************************/

static void sdcard_get_multibyte_response(SdcardHandle *h, uint8_t *data, uint8_t size) { //this function is used to geting type R2 and R3 responds
	uint8_t i, j = 0;
	while (j < 64) { // can try recognize first byte for max 64 times
		if (sdcard_get_byte(h) & 0x80) {
			for (i = 0; i < size; ++i)
				data[i] = sdcard_get_byte(h);
			break;
		}
		++j;
	}
}


/**************************************************************
 *********** GLOBAL FUNCTIONS *********
 **************************************************************/


/**************************************************************
 *********** initialization of sdcard registers****************
 **************************************************************/

void sdcard_reg_init(SdcardHandle *h, sdRegisters *reg) {
	//sdcard_get_cid(h, reg->cid); //useless
	sdcard_get_csd(h, reg->csd); //register contains configuration information required to access the card data
	//sdcard_get_ocr(h, reg->ocr); //power supply voltages, def 2,7-3,5 V
	sdcard_get_status(h, reg->status);
}


/****************************************************************************************************************************************************
 *********** sdcard initialization function to work in SPI mode, function also sets the size of data block and initialize internal registers *********
 ****************************************************************************************************************************************************/

SdErrorT sdcard_init(SdcardHandle *h, sdRegisters *reg) {
	uint8_t *data = 0;
	uint16_t timeout = 0;
	uint8_t i, dummy;

	SD_CS_bb = 1;

	for (i = 0 ; i < 10; i++)
		sdcard_spi_write_byte(h, 0xFF);

	SD_CS_bb = 0;

	sdcard_send_cmd(h, GO_IDLE_STATE, 0x00, 0x95);
	while ((sdcard_get_response(h) != R1b) && (timeout < 64)) {
		sdcard_send_cmd(h, GO_IDLE_STATE, 0x00, 0x95); //now CRC is of, sending a command 0
		timeout++;
	}

	if (timeout == 64)
		return SD_INIT_ERROR; //initialization error

	sdcard_send_cmd(h, SEND_OP_COND, R1, 0xFF);	//send a command 1
	while ((sdcard_get_response(h) != R1) && (timeout < 4096)) {//if no response, try one more
		sdcard_send_cmd(h, SEND_OP_COND, 0x00, 0xFF); //now CRC is of, sending a command 1
		++timeout;
	}

	sdcard_reg_init(h, reg); //initialization of the registers

	timeout = 4096;

	sdcard_send_cmd(h, SET_BLOCKLEN, BLOCKLEN_512, 0xFF);//set the lengh of data blocks
	while ((sdcard_get_response(h) != R1) && (timeout != 0)) {
		sdcard_send_cmd(h, SET_BLOCKLEN, BLOCKLEN_512, 0xFF);
		--timeout;
	}

	sdcard_spi_write_byte(h, 0xFF);

	if (!timeout)
		return SD_BLOCK_SET_ERROR;

	sdcard_wait_for_ready(h);

	SD_CS_bb = 1;
	return SD_SUCCESS; //initialization success, card is ready to work
}

/********************************************************************************************************************************************
 *********** function reads a OCR internal register and saves the value under an external table of variables (for example tab[553]*******
 ********************************************************************************************************************************************/

void sdcard_get_ocr(SdcardHandle *h, uint8_t *data) {
	sdcard_send_cmd(h, READ_OCR, 0x00, 0xFF);
	sdcard_get_multibyte_response(h, data, 5);
}

/***************************************************************************************************************************************************************
 *********** function reads a CID internal register and saves the value under an external table of variables (for example tab[553], CID cannot be modificated*******
 ***************************************************************************************************************************************************************/

void sdcard_get_cid(SdcardHandle *h, uint8_t *data) { // imposible to read the contents of this register when the card is in idle state
	sdcard_send_cmd(h, SEND_CID, 0x00, 0xFF);
	sdcard_get_multibyte_response(h, data, 16);
}

/****************************************************************************************************************************************
 *********** function reads a CSD internal register and saves the value under an external table of variables (for example tab[553]*******
 ****************************************************************************************************************************************/

void sdcard_get_csd(SdcardHandle *h, uint8_t *data) { // imposible to read the contents of this register when the card is in idle state
	sdcard_send_cmd(h, SEND_CSD, 0x00, 0xFF);
	sdcard_get_multibyte_response(h, data, 16);
}

/**********************************************************************************************************************************
 *********** function reads a STATUS register and saves the value under an external table of variables (for example tab[553])******
 **********************************************************************************************************************************/

void sdcard_get_status(SdcardHandle *h, uint8_t *data) {
	sdcard_send_cmd(h, SEND_STATUS, 0x00, 0xFF);
	sdcard_get_multibyte_response(h, data, 2);
}

/*********************************************************************
 *********** function writes the single data block on the SDCard******
 *********************************************************************/

SdErrorT sdcard_sector_write(SdcardHandle *h, uint32_t SectorAddress,
		uint8_t *Buffer) { //Buffer - unsigned char table of 512 elements, SectorAdress - number of the data sector

	SD_CS_bb = 0;

	uint8_t data_resp, i;
	uint16_t timeout;
	timeout = 4096;

	do {
		sdcard_send_cmd(h, WRITE_SINGLE_BLOCK, (SectorAddress << 9), 0xFF);	//sending command 24 - adress is multiple of 512
		data_resp = sdcard_get_response(h); //buffer, data - doesn't matter
		--timeout;
	} while ((data_resp != R1) && (timeout != 0));

	if (timeout == 0) {
		return SD_RESPONSE_ERROR;
	}


	sdcard_spi_write_byte(h, 0xFF);

	//begin of data writing

	sdcard_spi_write_byte(h, DATA_START_TOKEN); // write start data token

	sdcard_spi_send_frame(h, Buffer, 512);

	sdcard_spi_write_byte(h, 0xFF); //   first CRC byte
	sdcard_spi_write_byte(h, 0xFF); //	 second CRC byte

	//end of data writing
	timeout = 16;
	do {
		data_resp = sdcard_get_response(h);
		--timeout;
	} while (((data_resp & 0x0F) == DATA_ACCEPTED) && (timeout != 0));

	if (timeout == 0) {
		return DATA_WRT_ERR;
	}

	sdcard_wait_for_ready(h);

	SD_CS_bb = 1;

	return SD_SUCCESS;
}

/********************************************************************************
 *********** function reads the single data block (512 bytes) on the SDCard******
 ********************************************************************************/

SdErrorT sdcard_sector_read(SdcardHandle *h, uint32_t SectorAddress,
		uint8_t *Buffer) {
	uint8_t data_token, data_resp;
	uint16_t i, timeout;
	timeout = 4096;

	SD_CS_bb = 0;

	do {
		sdcard_send_cmd(h, READ_SINGLE_BLOCK, (SectorAddress << 9), 0xFF);//sending command 24 - adress is multiple of 512
		data_resp = sdcard_get_response(h); //buffer, data - doesn't matter
		--timeout;
	} while ((data_resp != R1) && (timeout != 0));

	if (timeout == 0) {
		SD_CS_bb = 1;
		return SD_RESPONSE_ERROR;
	}
	timeout = 4096;
	do {
		data_token = sdcard_get_byte(h);
		--timeout;
	} while ((data_token != DATA_START_TOKEN) && (timeout != 0)); // wait for the start byte 0xEF

	if (timeout == 0) {
		SD_CS_bb = 1;
		return SD_RESPONSE_ERROR;
	}

	for (i = 0; i < 512; ++i)		//get data
		Buffer[i] = sdcard_get_byte(h);

	sdcard_get_byte(h); //CRC - first byte
	sdcard_get_byte(h); //CRC - second byte

	sdcard_wait_for_ready(h);

	SD_CS_bb = 1;

	return SD_SUCCESS;
}

/****************************************************************************************************************
 *********** function erase data blocks from start till end address (multiple of 512 bytes) from the SDCard******
 ****************************************************************************************************************/

SdErrorT sdcard_sector_erase(SdcardHandle *h, uint32_t StartAddress,
		uint32_t EndAddress) {
	uint8_t *data = 0;
	uint8_t data_resp;
	uint16_t timeout;
	timeout = 4096;

	SD_CS_bb = 0;

	do {
		sdcard_send_cmd(h, ERASE_WR_BLK_START_ADDR, (StartAddress << 9), 0xFF);	//sending command 24 - adress is multiple of 512
		data_resp = sdcard_get_response(h); //buffer, data - doesn't matter
		--timeout;
	} while ((data_resp != R1) && (timeout != 0));

	if (timeout == 0) {
		SD_CS_bb = 1;
		return SD_RESPONSE_ERROR;
	}
	timeout = 4096;

	do {
		sdcard_send_cmd(h, ERASE_WR_BLK_END_ADDR, (EndAddress << 9), 0xFF);	//sending command 24 - adress is multiple of 512
		data_resp = sdcard_get_response(h); //buffer, data - doesn't matter
		--timeout;
	} while ((data_resp != R1) && (timeout != 0));

	if (timeout == 0) {
		SD_CS_bb = 1;
		return SD_RESPONSE_ERROR;
	}

	timeout = 128;
	sdcard_send_cmd(h, ERASE, 0x00, 0xFF); //erase blocks, address - doesn't matter
	do {
		data_resp = sdcard_get_response(h); //buffer, data - doesn't matter
		--timeout;
	} while ((data_resp != R1) && (timeout != 0)); // wait for a proper response

	sdcard_wait_for_ready(h);

	SD_CS_bb = 1; //end

	return SD_SUCCESS;
}

/**********************************************************************************************************
 *********** function reads amount data blocks pointed in TotalBlocks from start block (StartAddress)******
 **********************************************************************************************************/

SdErrorT sdcard_multiple_read(SdcardHandle *h, uint32_t StartAddress,
		uint32_t TotalBlocks, uint8_t *Buffer) {
	uint8_t data_token;
	uint32_t timeout;
	uint32_t i, j, a, b;
	timeout = 4096;

	j = 0;
	a = 0;
	b = 512;

	SD_CS_bb = 0;

	do {
		sdcard_send_cmd(h, READ_MULTI_BLOCK, (StartAddress << 9), 0xFF); //set start block
		data_token = sdcard_get_response(h);//wait for the proper answer
		--timeout;
	} while ((data_token != R1) && (timeout != 0));

	if (timeout == 0) {
		SD_CS_bb = 1;
		return SD_RESPONSE_ERROR;
	}

	for (i = 0; i < TotalBlocks; ++i) {

		timeout = 64;
		do {
			data_token = sdcard_get_byte(h);
			//	--timeout;
		} while ((data_token != DATA_START_TOKEN) && (timeout != 0)); // wait for the start byte 0xEF

		if (timeout == 0) {
			SD_CS_bb = 1;
			return SD_DATA_TOKEN_ERROR;
		}

		for (j = a; j < b; ++j) {
			Buffer[j] = sdcard_get_byte(h); //get data
		}
		sdcard_get_byte(h); //crc 2 bytes
		sdcard_get_byte(h);

		a = j;
		b += 512;
	}

	timeout = 8;
	sdcard_send_cmd(h, STOP_TRANSMISSION, 0, 0); //stop transmission command
	do {
		data_token = sdcard_get_byte(h);
		--timeout;
	} while (((data_token != R1b)) && (timeout != 0));
	sdcard_wait_for_ready(h);
	if (timeout == 0) {
		SD_CS_bb = 1;
		return SD_RESPONSE_ERROR;
	}

	timeout = 0xfffe;
	sdcard_wait_for_ready(h);

	sdcard_spi_write_byte(h, 0xFF);
	sdcard_spi_write_byte(h, 0xFF);
	return SD_SUCCESS;
}

/*************************************************************************************************************
 *********** function writes multiple data blocks pointed in TotalBlocks from start block (StartAddress)******
 *************************************************************************************************************/

SdErrorT sdcard_multiple_write(SdcardHandle *h, uint32_t StartAddress,
		uint32_t TotalBlocks, uint8_t *Buffer) {
	uint8_t data_token;
	uint16_t timeout;
	uint32_t i, j, a, b;
	timeout = 4096;

	j = 0;
	a = 0;
	b = 512;

	SD_CS_bb = 0;

	do {
		sdcard_send_cmd(h, WRITE_MULTI_BLOCK, (StartAddress << 9), 0xFF); //send the command
		data_token = sdcard_get_response(h);	//check the response
		--timeout;
	} while ((data_token != R1) && (timeout != 0)); //wait for a proper response

	if (timeout == 0) {
		SD_CS_bb = 1;
		return SD_RESPONSE_ERROR;
	}
	for (i = 0 ; i < 64; i++)
		sdcard_spi_write_byte(h, 0xFF);							//require break

	for (i = a; i < TotalBlocks; ++i) {
		a = j;
		sdcard_spi_write_byte(h, DATA_MULT_WRT_START_TOK); //first, send a start data token 0xFC

		for (j = a; j < b; ++j)			//second, send a content of the buffer
			sdcard_spi_write_byte(h, Buffer[j]);

		sdcard_spi_write_byte(h, 0xFF); //CRC
		sdcard_spi_write_byte(h, 0xFF); //CRC

		if ((sdcard_get_byte(h) & 0x1f) != DATA_ACCEPTED) {
			SD_CS_bb = 1; //recieve data accepted token
			return SD_DATA_TOKEN_ERROR; //return SD_RESPONSE_ERROR;
		}

		b += 512;

		timeout = 0xfffe;

		sdcard_wait_for_ready(h);
	}

	sdcard_spi_write_byte(h, DATA_MULT_WRT_STOP_TOK);	//stop data token
	sdcard_spi_write_byte(h, 0xFF);

	sdcard_wait_for_ready(h);

	return SD_SUCCESS;
}

